package chapter21.newcom;

import java.util.*;
import java.util.concurrent.*;

class Horse implements Runnable {
	private static int counter = 0;
	private final int id = counter++;
	private int strides = 0;
	private static Random rand = new Random(47);
	private static CyclicBarrier barrier;
	
	public Horse(CyclicBarrier b) {
		barrier = b;
	}
	
	public synchronized int getStrides() {
		return strides;
	}
	
	@Override
	public void run() {
		try {
			while(!Thread.interrupted()) {
				synchronized (this) {
					strides += rand.nextInt(3);
				}
			}
			barrier.await();
		} catch(InterruptedException e) {
			
		} catch (BrokenBarrierException e) {
			// This one we want to know about
			throw new RuntimeException(e);
		}
	}
	
	@Override
	public String toString() {
		return "Horse " + id + " ";
	}
	
	public String tracks() {
		StringBuilder s = new StringBuilder();
		for(int i = 0; i < getStrides(); i++)
			s.append("*");
		s.append(id);
		return s.toString();
	}
}

public class HorseRace {
	static final int FINIFH_LINE = 75;
	private List<Horse> horses = new ArrayList<>();
	private ExecutorService exec = Executors.newCachedThreadPool();
	private CyclicBarrier barrier;
	
	public HorseRace(int nHorses, int pause) {
		barrier = new CyclicBarrier(nHorses, new Runnable() {
			@Override
			public void run() {
				StringBuilder s = new StringBuilder();
				for(int i = 0; i < FINIFH_LINE; i++)
					s.append("="); // The fence on the racetrack
				System.out.println(s);
				for(Horse horse: horses) 
					System.out.println(horse.tracks());
				for(Horse horse: horses) 
					if(horse.getStrides() >= FINIFH_LINE) {
						System.out.println(horse + "won!");
						exec.shutdownNow();
						return;
					}
				try {
					TimeUnit.MILLISECONDS.sleep(pause);
				} catch (InterruptedException e) {
					System.out.println("barrier-action sleep interrrupted");
				}
			}
		});
		
		for(int i = 0; i < nHorses; i++) {
			Horse horse = new Horse(barrier);
			horses.add(horse);
			exec.execute(horse);
		}
	}
	
	public static void main(String[] args) {
		int nHorses = 7;
		int pause = 20;
		new HorseRace(nHorses, pause);
	}
}
